package org.smartboot.compare;

import org.smartboot.compare.utils.ArgumentUtils;
import org.smartboot.compare.utils.InternalClassUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author qinluo
 * @date 2024-08-13 22:39:54
 * @since 1.1.2
 */
public class GlobalConfiguration {

    private static final List<ClassMatcher> useDefaultEqualsBehaviorClassMatchers = new ArrayList<>(8);
    private static final CollectionClassMatcher collectionClassMatcher = new CollectionClassMatcher();
    public static boolean getFieldObjectUseMethodHandle = false;

    static {
        addUseDefaultEqualsBehaviorClassMatcher(((type, ctx) -> InternalClassUtils.isSimple(type)));
        addUseDefaultEqualsBehaviorClassMatcher(collectionClassMatcher);
        addUseDefaultEqualsBehaviorWithSubclassOf(Number.class);
    }

    public static boolean isUseDefaultEqualsBehavior(Class<?> type, ComparatorContext<?> ctx) {
        for (ClassMatcher classMatcher : useDefaultEqualsBehaviorClassMatchers) {
            if (classMatcher.match(type, ctx)) {
                return true;
            }
        }

        return false;
    }

    public static void addUseDefaultEqualsBehaviorClass(Class<?> type) {
        ArgumentUtils.notNull(type, "not null");
        collectionClassMatcher.useDefaultEqualsBehaviorClasses.put(type, type);
    }

    public static void removeUseDefaultEqualsBehaviorClass(Class<?> type) {
        ArgumentUtils.notNull(type, "not null");
        collectionClassMatcher.useDefaultEqualsBehaviorClasses.remove(type);
    }

    public static void addUseDefaultEqualsBehaviorClassMatcher(ClassMatcher classMatcher) {
        ArgumentUtils.notNull(classMatcher, "not null");
        useDefaultEqualsBehaviorClassMatchers.add(classMatcher);
    }

    public static void removeUseDefaultEqualsBehaviorClassMatcher(ClassMatcher classMatcher) {
        ArgumentUtils.notNull(classMatcher, "not null");
        useDefaultEqualsBehaviorClassMatchers.remove(classMatcher);
    }

    public static void addUseDefaultEqualsBehaviorWithSubclassOf(Class<?> type) {
        ArgumentUtils.notNull(type, "not null");
        if (type == Object.class) {
            throw new IllegalArgumentException("Cannot be object class.");
        }
        useDefaultEqualsBehaviorClassMatchers.add(new SubclassMatcher(type));
    }

    public interface ClassMatcher {
        boolean match(Class<?> type, ComparatorContext<?> ctx);
    }

    static class CollectionClassMatcher implements ClassMatcher {

        final Map<Class<?>, Class<?>> useDefaultEqualsBehaviorClasses = new ConcurrentHashMap<>();

        @Override
        public boolean match(Class<?> type, ComparatorContext<?> ctx) {
            return useDefaultEqualsBehaviorClasses.containsKey(type);
        }
    }

    public static class SubclassMatcher implements ClassMatcher {

        private final Class<?> parent;

        public SubclassMatcher(Class<?> parent) {
            this.parent = parent;
        }

        @Override
        public boolean match(Class<?> type, ComparatorContext<?> ctx) {
            return parent.isAssignableFrom(type);
        }

        @Override
        public int hashCode() {
            return parent.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof SubclassMatcher) {
                return ((SubclassMatcher) obj).parent == parent;
            }
            return false;
        }
    }

}
